;; lxrtags.el --- lookup kernel tags from lxr
;;
;; Copyright (C) 2015 Octagram Sun
;;
;; Author: 隐星璇 Octagram <octagram@qq.com>
;;
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;;
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; * Installation:
;; Copy lxrtags.el to your load-path and add following statement to your .emacs:
;; (require 'lxrtags)
;;
;; then M-x lxrtags-mode to use this plugin.
;; If you want to autoload the plugin, add following statement to your .emacs:
;; (global-lxrtags-mode 1)
;;
;; * 安装
;; 拷贝lxrtags.el到你的load-path路径中，然后在你的.emacs配置文件中增加：
;; (require 'lxrtags)
;;
;; 在emacs中可以使用 M-x lxrtags-mode 来启动此插件
;; 也可以配置为自动启动，在.emacs中增加：
;; (global-lxrtags-mode 1)
;; 默认浏览器：eww
;; lxr url：free-electrons
;; 如果需要修改浏览器或词典，配置如下
;; (setq lxrtags-browser "w3m") ;; 设置浏览器为w3m
;; ** 目前支持的浏览器取值：
;;    w3m: "w3m"
;;    eww: "eww"
;; (setq lxrtags-url free-electrons) ;; 设置lxr url为free-electrons
;; ** 目前支持的lxr url：
;;    free-electrons
;; (global-lxrtags-mode 1) ;;设置完成后再开启模式
;;
;; * 热键
;; M-s M-l h 查询光标处的单词
;; M-s M-l w 从minibuffer输入单词并查询
;;

(defgroup lxrtags nil
  "Group for lxrtags minor mode."
  :group 'tools
  :prefix "lxrtags-")

(defvar lxrtags-mode nil "Non-nil if lxrtags mode is enabled.")
(make-variable-buffer-local 'lxrtags-mode)

(defvar lxrtags-mode-hook nil "lxrtags minor mode hook")

;; ******** ******** ******** url ******** ******** ********
(defconst free-electrons "http://lxr.free-electrons.com/ident?i="
  "free-electrons")
(defvar lxrtags-url free-electrons "lxr url")

;; ******** ******** ******** browser ******** ******** ********
(defconst bw-w3m-buf "*w3m*")
(defconst bw-w3m-cmd 'w3m-goto-url)
(defconst bw-eww-buf "*eww*")
(defconst bw-eww-cmd 'eww)
(defvar lxrtags-browser-buf bw-eww-buf "default browser buffer name")
(defvar lxrtags-browser-cmd bw-eww-cmd "cmd to launch browser")
(defvar lxrtags-browser "eww" "default browser")

;; ******** ******** ******** keymap ******** ******** ********
(defvar lxrtags-mode-map (make-sparse-keymap)
  "Keymap used in lxrtags mode.")
(setq lxrtags-mode-map-prefix (make-sparse-keymap))
(define-prefix-command 'lxrtags-mode-map-cmd 'lxrtags-mode-map)
(progn
  (define-key lxrtags-mode-map-prefix (kbd "M-s M-l") 'lxrtags-mode-map-cmd))
(progn
  (define-key lxrtags-mode-map (kbd "h") 'lxrtags-find-tag-from-here)
  (define-key lxrtags-mode-map (kbd "w") 'lxrtags-find-tag))

;; ******** ******** ******** functions ******** ******** ********
;; ******** lxrtags-current-token ********
(defconst lxrtags-definition-regexp "#[ \t]*define[ \t]+\\|ENTRY(\\|ALTENTRY("
  "Regexp matching tag definition name.")
(defconst lxrtags-symbol-regexp "[A-Za-z_][A-Za-z_0-9]*"
  "Regexp matching tag name.")
(defun lxrtags-match-string (n)
  (buffer-substring (match-beginning n) (match-end n)))
(defun lxrtags-current-token ()
  "get token from current point"
  (cond
   ((looking-at "[0-9A-Za-z_]")
    (while (and (not (bolp)) (looking-at "[0-9A-Za-z_]"))
      (forward-char -1))
    (if (not (looking-at "[0-9A-Za-z_]")) (forward-char 1)))
   (t
    (while (looking-at "[ \t]")
      (forward-char 1))))
  (if (and (bolp) (looking-at lxrtags-definition-regexp))
      (goto-char (match-end 0)))
  (if (looking-at lxrtags-symbol-regexp)
      (lxrtags-match-string 0)
    nil))

;; ******** lxrtags-find-tag-from-here ********
(defun lxrtags-find-tag-from-here ()
  "lookup tag at current point on lxr."
  (interactive)
  (save-restriction
    (let (tag)
      (setq tag (lxrtags-current-token))
      (funcall lxrtags-browser-cmd (concat lxrtags-url tag))
      (if (not (string= (buffer-name) lxrtags-browser-buf))
          (switch-to-buffer-other-window lxrtags-browser-buf)
        )
      ;;    (hl-line-mode lxrtags-browser-buf)
      (sit-for 1))))

;; ******** lxrtags-find-tag ********
(defun lxrtags-find-tag ()
  "lookup tag from minibuffer on lxr."
  (interactive)
  (save-restriction
    (let (tag input prompt)
      (setq tag (lxrtags-current-token))
      (if tag
          (setq prompt (concat "tag: (default " tag ") "))
        (setq prompt "tag: "))
      (setq input (read-from-minibuffer prompt))
      (if (not (equal "" input))
          (setq tag input))
      (funcall lxrtags-browser-cmd (concat lxrtags-url tag))
      (if (not (string= (buffer-name) lxrtags-browser-buf))
          (switch-to-buffer-other-window lxrtags-browser-buf))
      ;;    (hl-line-mode lxrtags-browser-buf)
      (sit-for 1))))

;; ******** minor mode ********
(define-minor-mode lxrtags-mode
  "Minor mode to lookup lxrtags"
  1 " hl-p" lxrtags-mode-map-prefix
  :global t
  :group 'lxrtags
  (if (string= lxrtags-browser "eww")
      (progn
        (setq lxrtags-browser-buf bw-eww-buf)
        (setq lxrtags-browser-cmd bw-eww-cmd)))
  (if (string= lxrtags-browser "w3m")
      (progn
        (setq lxrtags-browser-buf bw-w3m-buf)
        (setq lxrtags-browser-cmd bw-w3m-cmd)))
  (run-hooks 'lxrtags-mode-hook))

(define-globalized-minor-mode global-lxrtags-mode
  lxrtags-mode
  (lambda () (lxrtags-mode 1)))

(provide 'lxrtags)
